package com.scs.application.modules.dept.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.query.QueryContext;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.utils.BeanUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UserUtils;
import com.scs.application.modules.base.entity.Matr;
import com.scs.application.modules.base.mapper.MatrMapper;
import com.scs.application.modules.base.util.MatrPassUtil;
import com.scs.application.modules.dept.entity.Use;
import com.scs.application.modules.dept.entity.UseDtl;
import com.scs.application.modules.dept.mapper.UseDtlMapper;
import com.scs.application.modules.dept.mapper.UseMapper;
import com.scs.application.modules.dept.service.UseDtlService;
import com.scs.application.modules.purchase.enums.PurchaseType;
import com.scs.application.modules.wm.entity.Stock;
import com.scs.application.modules.wm.entity.StockLock;
import com.scs.application.modules.wm.entity.StockZero;
import com.scs.application.modules.wm.entity.TertiaryStock;
import com.scs.application.modules.wm.enums.LockBusType;
import com.scs.application.modules.wm.mapper.TertiaryStockMapper;
import com.scs.application.modules.wm.service.StockLockService;
import com.scs.application.modules.wm.service.StockService;
import com.scs.application.modules.wm.service.StockZeroService;
import com.scs.application.modules.wm.utils.UniqueCodeUtils;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 */
@Service
public class UseDtlServiceImpl extends BaseServiceImpl<UseDtlMapper, UseDtl> implements UseDtlService {
    @Autowired
    private StockService stockService;
    @Autowired
    private StockZeroService stockZeroService;
    @Autowired
    private StockLockService stockLockService;
    @Autowired
    private UseMapper useMapper;
    @Autowired
    MatrPassUtil matrPassUtil;
    @Resource
    private MatrMapper matrMapper;
    @Resource
    private TertiaryStockMapper tertiaryStockMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveByStock(String stockId, String useId, boolean fromFsd) {
        // 新增消耗单明细数据
        Stock stock = stockService.getExpStockById(stockId, false);
        if (stock == null) {
            throw new BusinessException("未找到库存记录");
        }
        if (!fromFsd && stock.getFlagOmoc()) {
            throw new BusinessException("一物一码耗材不支持科室直接消耗，您可以使用病人消耗功能");
        }

        List<StockLock> sn = stockLockService.list(Wrappers.<StockLock>query().eq("id", stock.getId()));
        if (!sn.isEmpty() || stock.getPackageQty() == 0) {
            throw new BusinessException("唯一码当前库存为0.0");
        }
        //是否禁用耗材校验--科室消耗
        List<String> matrIds = new ArrayList<>();
        matrIds.add(stock.getMatrId());
        matrPassUtil.ckeckMatrPass("deptuse", matrIds);
        // 消耗的是试剂，试剂组必填
//        Matr matr = matrMapper.selectById(stock.getMatrId());
//        if (PurchaseType.REAGENT.getKey().equals(matr.getBaseType())) {
//            Use use = useMapper.selectById(useId);
//            if (use != null) {
//                if (StringUtils.isEmpty(use.getReagentTeamId())) {
//                    throw new BusinessException("请选择试剂组");
//                }
//            }
//        }

        UseDtl useDtl = new UseDtl();
        BeanUtils.copyProperties(stock, useDtl);
        useDtl.setId(null);
        useDtl.setUseId(useId).setStockId(stock.getId()).setMatrId(stock.getMatrId())
                .setMatrName(stock.getMatrName()).setMatrSpec(stock.getMatrSpec())
                .setPackageQty(stock.getPackageQty()).setPackageUnit(stock.getPackageUnit())
                .setMatrUnit(stock.getSkuUnit()).setMatrModel(stock.getMatrModel())
                .setSkuPrice(stock.getSkuPrice()).setSkuQty(stock.getSkuQty())
                .setAmount(stock.getAmount()).setLot(stock.getLot()).setExpDate(stock.getExpDate())
                .setSnNo(stock.getSn()).setRfid(stock.getRfid()).setSupplierId(stock.getSupplierId())
                .setSupplierName(stock.getSupplierName()).setMfgName(stock.getMfgName())
                .setRemark("消耗出库").setUser(UserUtils.currentUser() != null ? UserUtils.currentUser().getName() : "")
                .setUseTime(new Date());
        boolean b = super.saveOrUpdate(useDtl);

        // 锁库存
        String uniqueCode = StringUtils.isBlank(stock.getSn()) ? stock.getRfid() : stock.getSn();
        stockLockService.lock(LockBusType.USE, stock.getId(),
                stock.getWarehouseId(), uniqueCode, UniqueCodeUtils.getUniqueCodeType(uniqueCode), "消耗出库单锁库存");

        // 修改消耗单金额
        updateUseAmount(useId);

        return b;
    }

    @Override
    public boolean removeCascadeByIds(Collection<? extends Serializable> idList) {
        if (CollectionUtils.isNotEmpty(idList)) {
            UseDtl useDtl = this.getById(idList.iterator().next());
            boolean result = super.removeCascadeByIds(idList);
            List<String> types = baseMapper.getType(useDtl.getUseId());
            if (types.stream().noneMatch(type -> PurchaseType.REAGENT.getKey().equals(type))) {
                // 消耗明细不包含试剂时，置空试剂组id
                useMapper.update(null, Wrappers.<Use>update().set("reagent_team_id", null).eq("id", useDtl.getUseId()));
            }
            return result;
        }
        return false;
    }

    /**
     * 根据上次消耗记录产生冲红记录
     *
     * @param stockIds
     * @param warehouseId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void useRedByStockIds(List<String> stockIds, Use use, String warehouseId, boolean fromFsd) {
        List<UseDtl> redUseDtls = Lists.newArrayListWithCapacity(stockIds.size());
        List<Stock> stocks = stockService.list(Wrappers.<Stock>query().in("id", stockIds));
        if (!stocks.isEmpty()) {
            throw new BusinessException("sn【%s】还存在于库存表中，不允许冲红！", stocks.get(0).getSn());
        }
        List<StockZero> stockZeros = stockZeroService.list(Wrappers.<StockZero>query().in("id", stockIds));
        List<String> matrIds = stockZeros.stream().map(StockZero::getMatrId).collect(Collectors.toList());
        List<Matr> matrList = matrMapper.selectBatchIds(matrIds);
        for (StockZero stockZero: stockZeros) {
            if (!fromFsd && stockZero.getFlagOmoc()) {
                throw new BusinessException("一物一码耗材不支持科室直接消耗，您可以使用病人消耗功能");
            }

            List<UseDtl> useDtls = this.getUseDtlBySn(stockZero.getSn(), warehouseId);
            if (useDtls == null || useDtls.size() == 0) {
                throw new BusinessException("未找到" + stockZero.getSn() + "的消耗记录，不允许冲红");
            }
            boolean isReagent = matrList.stream().anyMatch(matr -> stockZero.getMatrId().equals(matr.getId()) && PurchaseType.REAGENT.getKey().equals(matr.getBaseType()));
            if (isReagent) {
                // 查询三级库库存
                TertiaryStock tertiaryStock = tertiaryStockMapper.selectOne(Wrappers.<TertiaryStock>query().eq("sn", stockZero.getSn()));
                if (tertiaryStock == null) {
                    throw new BusinessException("未找到"+ stockZero.getSn() +"的三级库库存，不允许冲红");
                } else if (tertiaryStock.getPackageQty() == 0) {
                    throw new BusinessException(tertiaryStock.getSn() + "已被消耗，不允许冲红");
                }
                // 设置试剂组
                if (use.getReagentTeamId() == null) {
                    use.setReagentTeamId(tertiaryStock.getReagentTeamId());
                } else if (!use.getReagentTeamId().equals(tertiaryStock.getReagentTeamId())){
                    throw new BusinessException("不允许选择其他专业组的试剂");
                }
            }

            UseDtl lastUseDtl = useDtls.get(0);
            Use lastUse = useMapper.selectById(lastUseDtl.getUseId());
            if (GlobalConsts.YES.equals(lastUse.getFlagUseRedType())) {
                throw new BusinessException("SN:" + stockZero.getSn() + "已经被冲红，不允许重复冲红");
            } else if (!lastUse.getFlowStatus().equals("end")) {
                throw new BusinessException("SN:" + stockZero.getSn() + "的消耗记录尚未产生（流程未结束），不允许冲红");
            }
            UseDtl redUseDtl = new UseDtl();
            BeanUtils.copyProperties(lastUseDtl, redUseDtl);
            redUseDtl.setUseId(use.getId()).setRemark("消耗出库冲红").setUser(UserUtils.currentUser().getName())
                    .setUseTime(new Date()).setBillId(null).setBillItemId(null).setId(null);
            redUseDtls.add(redUseDtl);

        }

        super.saveBatch(redUseDtls);
        // 修改消耗单金额
        updateUseAmount(use.getId(), use.getReagentTeamId());
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveByStockIds(List<String> stockIds, String useId, boolean fromFsd) {
        if (stockIds.isEmpty()) {
            throw new BusinessException("没有可用消耗单");
        }
        for (String stockId : stockIds) {
            saveByStock(stockId, useId, fromFsd);
        }
    }

    @Override
    public List<UseDtl> listCurrDateAndDept() {
        List<Use> useList = useMapper.selectList(Wrappers.<Use>query()
                .ge("gmt_create", LocalDate.now())
                .eq("dept_id", UserUtils.currentUser().getOfficeId())
                .eq("flow_status", "end")
        );
        if (useList.isEmpty()) {
            throw new BusinessException("没有可用消耗单据");
        }
        List<UseDtl> usedtlList = this.list(Wrappers.<UseDtl>query()
                .in("use_id", useList.stream().map(o -> o.getId()).collect(Collectors.toList())));
        return usedtlList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveByStockIds(List<String> stockIds, String useId, String warehouseId, boolean fromFsd) {
        Use use = useMapper.selectById(useId);
        if (use == null) {
            return;
        }
        // 重复扫描，删除明细
        List<UseDtl> useDtls = this.list(Wrappers.<UseDtl>query().eq("use_id", useId).in("stock_id", stockIds));
        if (!useDtls.isEmpty()) {
            this.removeByIds(useDtls.stream().map(UseDtl::getId).collect(Collectors.toList()));
            List<String> types = baseMapper.getType(useId);
            if (types.stream().noneMatch(type -> PurchaseType.REAGENT.getKey().equals(type))) {
                // 消耗明细不包含试剂时，置空试剂组id
                useMapper.update(null, Wrappers.<Use>update().set("reagent_team_id", null).eq("id", useId));
            }
            return;
        }

        // 判断是否为冲红单据
        if ("1".equals(use.getFlagUseRedType().toString())) {
            useRedByStockIds(stockIds, use, warehouseId, fromFsd);
        } else {
            saveByStockIds(stockIds, useId, fromFsd);
        }
    }


    private void updateUseAmount(String useId) {
        Double total = calcUseAmount(useId);
        // 修改消耗单总金额
        useMapper.update(null, Wrappers.<Use>update().set("amount", total).eq("id", useId));
        ;
    }

    // 修改消耗单总金额并修改其他字段
    private void updateUseAmount(String useId, String reagentTeamId) {
        Double total = calcUseAmount(useId);
        // 修改消耗单总金额
        useMapper.update(null, Wrappers.<Use>update().set("amount", total).set("reagent_team_id", reagentTeamId).eq("id", useId));
        ;

    }

    private Double calcUseAmount(String useId) {
        Double totalAmount = this.getObj(Wrappers.<UseDtl>query().select("sum(amount)").eq("use_id", useId), v -> {
            if (v == null) {
                return 0.0;
            }
            return Double.valueOf(v.toString());
        });
        return totalAmount;
    }

    @Override
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        if (idList.isEmpty()) {
            return true;
        }
        List<UseDtl> useDtls = this.list(Wrappers.<UseDtl>query().in("id", idList));
        // 判断如果是冲红单据，不进行解锁库存操作
        String useId = useDtls.stream().findFirst().get().getUseId();
        Use use = useMapper.selectById(useId);
        if (GlobalConsts.YES.equals(use.getFlagUseRedType())) {
            boolean b = super.removeByIds(idList);
            // 修改消耗单金额
            updateUseAmount(useId);
            return b;
        }
        List<String> stockIds = useDtls.stream().map(o -> o.getStockId()).collect(Collectors.toList());
        List<Stock> stockList = stockService.list(Wrappers.<Stock>query().in("id", stockIds));
        for (Stock stock : stockList) {
            // 解锁库存
            stockLockService.unlock(stock.getId());
        }
        boolean b = super.removeByIds(idList);
        // 修改消耗单金额

        updateUseAmount(useId);
        return b;
    }


    public boolean saveItemByCode(String stockId, String useId) {

        Stock stock = stockService.getById(stockId);
        //科室消耗冲红需要库存为0且非一物一码耗材
        if (stock == null) {
            throw new BusinessException("未找到相关库存信息");
        }
        if (stock.getFlagOmoc()) {
            throw new BusinessException("唯一码是一物一码耗材，不可消耗");
        }
        Use use = useMapper.selectById(useId);
        if (use == null) {
            throw new BusinessException("未找到消耗主单，可能已被移出");
        }
        //根据冲红类型进行校验
        if (stock.getPackageQty() > 0) {
            throw new BusinessException("库存不为0,不可进行冲红操作");
        }
        //获取冲红单据
        List<String> useIds = Lists.newArrayList();
        List<Use> uses = useMapper.selectList(new QueryWrapper<Use>().eq("flag_use_red_type", 1));
        if (!uses.isEmpty()) {
            useIds = uses.stream().map(o -> o.getId()).collect(Collectors.toList());
        }
        //获取消耗单据
        List<String> useIds1 = Lists.newArrayList();
        List<Use> uses1 = useMapper.selectList(new QueryWrapper<Use>().eq("flag_use_red_type", 0));
        if (!uses1.isEmpty()) {
            useIds1 = uses1.stream().map(o -> o.getId()).collect(Collectors.toList());
        }
        //获取所有冲红单据的明细的stock_id
        List<UseDtl> useDtls = this.list(new QueryWrapper<UseDtl>().in("use_id", useIds));
        List<String> useStocks = Lists.newArrayList();
        if (!useDtls.isEmpty()) {
            useStocks = useDtls.stream().map(o -> o.getStockId()).collect(Collectors.toList());
        }//获取所有消耗单据的明细的stock_id
        List<UseDtl> useDtls1 = this.list(new QueryWrapper<UseDtl>().in("use_id", useIds1).eq("stock_id", stockId));
        if (useDtls1.isEmpty()) {
            throw new BusinessException("该耗材未消耗无法冲红");
        }
        UseDtl useDtl = useDtls1.get(0);
        if (useStocks.contains(stockId)) {
            throw new BusinessException("该耗材已冲红");
        }
        UseDtl useDtl1 = new UseDtl();
        BeanUtils.copyProperties(stock, useDtl1);
        useDtl1.setId(null);
        useDtl1.setUseId(useId)
                .setStockId(stock.getId())
                .setMatrId(stock.getMatrId())
                .setMatrName(stock.getMatrName())
                .setMatrSpec(stock.getMatrSpec())
                .setMatrUnit(useDtl.getMatrUnit())
                .setPackageQty(useDtl.getPackageQty())
                .setPackageUnit(useDtl.getPackageUnit())
                .setSkuPrice(useDtl.getSkuPrice())
                .setSkuQty(useDtl.getSkuQty())
                .setAmount(useDtl.getAmount()).setSupplierId(stock.getSupplierId())
                .setSupplierName(stock.getSupplierName()).setMfgName(stock.getMfgName())
                .setLot(stock.getLot()).setExpDate(stock.getExpDate())
                .setSnNo(stock.getSn())
                .setRfid(stock.getRfid())
                .setRemark("消耗出库冲红")
                .setUser(UserUtils.currentUser().getName())
                .setUseTime(new Date());
        boolean b = super.saveOrUpdate(useDtl1);
        updateUseAmount(useId);
        return b;
    }

    @Override
    public IPage<UseDtl> pageForRedList(QueryContext queryContext) {
        return baseMapper.pageForRedList(queryContext.getPage(), queryContext.getWrapper(), queryContext.getParams());
    }

    public List<UseDtl> getUseDtlBySn(String sn, String warehouseId) {
        return baseMapper.getUseDtlBySn(sn, warehouseId);
    }

    @Override
    public String getType(String useId) {
        List<String> types = baseMapper.getType(useId);
        if (!types.isEmpty()) {
            if(types.stream().anyMatch(type -> PurchaseType.REAGENT.getKey().equals(type))) {
                return PurchaseType.REAGENT.getKey();
            }
        }
        return null;
    }
}
